package org.apache.ibatis.AAMyDemo.pagePlugin;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.BaseStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;

/**
 * mybatis分页插件拦截器
 *
 * @Author yangSen
 * @Date 2022年05月04日 15:50
 */
@Intercepts(@Signature(type = StatementHandler.class,
  method = "prepare",
  args = {Connection.class, Integer.class})
)
public class PageInterceptor implements Interceptor {

  /**
   * 拦截所走的方法
   *
   * @param invocation
   * @return
   *
   * @throws Throwable
   */
  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    //1. 判断请求参数是否继承了 page  对象
    StatementHandler target = (StatementHandler) invocation.getTarget();
    BoundSql boundSql = target.getBoundSql();


    // 参数
    Object parameterObject = boundSql.getParameterObject();

    // 判断是否继承了 page 对象
    Page page = null;
    // 单个参数处理
    if (parameterObject instanceof Page) {
      page = (Page) parameterObject;
    } else if (parameterObject instanceof Map) {
      // 多个参数处理 ,  mybatis 会将多个参数封装成map进行处理
      page = (Page) ((Map<?, ?>) parameterObject).values()
        .stream()
        .filter(v -> v instanceof Page)
        .findFirst()
        .orElse(null);
    }

    // 没有继承分页参数直接走
    if (page == null) {
      return invocation.proceed();
    }

    // 设置总条数
    page.setTotal(this.getCount(invocation, boundSql));

    // 修改原有的sql，改成分页的模式
    String pageSql = String.format("%s LIMIT %s , %s", boundSql.getSql(), page.offset(), page.getSize());
    SystemMetaObject.forObject(boundSql).setValue("sql", pageSql);
    return invocation.proceed();
  }

  /**
   * 获取总条数
   *
   * @param invocation 拦截对象
   * @param boundSql   sql元对象
   * @return
   */
  private int getCount(Invocation invocation, BoundSql boundSql) throws SQLException {
    // 获取数据库连接对象
    Connection connection = (Connection) invocation.getArgs()[0];

    // 拼接sql
    String totalSql = String.format("SELECT COUNT(1) FROM ( %s ) _PAGE", boundSql.getSql());
    System.err.println(totalSql);
    // 获取 Statement 对象，准备执行sql
    PreparedStatement preparedStatement = connection.prepareStatement(totalSql);


    // 当存在多个参数的时候，还需要通过参数处理器处理一下参数
    StatementHandler target = (StatementHandler) invocation.getTarget();
    target.parameterize(preparedStatement);

    // 执行sql
    ResultSet resultSet = preparedStatement.executeQuery();

    int count = 0;
    // 获取总条数
    if (resultSet.next()) {
      count = resultSet.getInt(1);
    }
    // 关闭连接
    resultSet.close();
    preparedStatement.close();

    return count;
  }


  @Override
  public Object plugin(Object target) {
    return Interceptor.super.plugin(target);
  }

  @Override
  public void setProperties(Properties properties) {
    System.out.println("执行 setProperties");
    Interceptor.super.setProperties(properties);
  }
}
